@file:Suppress("unused", "MemberVisibilityCanBePrivate", "DEPRECATION")

package com.evendai.loglibrary

import android.annotation.SuppressLint
import android.app.Application
import android.os.Build
import android.os.Environment
import android.preference.PreferenceManager
import android.text.format.DateFormat
import android.util.Base64
import android.util.Log
import android.view.Gravity
import android.view.View
import android.widget.Toast
import cn.dazhou.contextholder.ContextHolder
import com.evendai.loglibrary.TimberConfig.configureLogbackByString
import org.jetbrains.annotations.NonNls
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.io.File
import java.io.PrintWriter
import java.io.StringWriter
import java.lang.Exception
import java.util.*
import java.util.regex.Pattern
import javax.crypto.Cipher
import javax.crypto.spec.SecretKeySpec

/** 懒人的日志工具  */
@Suppress("MemberVisibilityCanBePrivate")
class Timber private constructor() {

    init {
        throw AssertionError("No instances.") // 不允许实例化
    }

    /** 用于处理日志调用的外观。通过Timber.plant()方法安装实例  */
    abstract class Tree {

        /** 显式的tag，用于保存用户手动指定的tag */
        val explicitTag = ThreadLocal<String>()

        /** 取出用户手动设置的tag，使用完立即丢弃，即用户手动设置的tag只能作用于一次log输出 */
        open fun getTag(): String? {
            val tag = explicitTag.get()
            if (tag != null) {
                explicitTag.remove()
            }
            return tag
        }

        /** 记录一个使用可选格式args的verbose消息。  */
        open fun v(message: String, vararg args: Any) {
            prepareLog(Log.VERBOSE, null, message, *args)
        }

        /** 记录一个verbose的异常和使用可选格式args的消息。  */
        open fun v(t: Throwable, message: String, vararg args: Any) {
            prepareLog(Log.VERBOSE, t, message, *args)
        }

        /** 记录一个verbose异常。  */
        open fun v(t: Throwable) {
            prepareLog(Log.VERBOSE, t, null)
        }

        /** 记录一个使用可选格式args的debug消息。  */
        open fun d(message: String, vararg args: Any) {
            prepareLog(Log.DEBUG, null, message, *args)
        }

        /** 记录一个debug异常和使用可选格式args的消息。  */
        open fun d(t: Throwable, message: String, vararg args: Any) {
            prepareLog(Log.DEBUG, t, message, *args)
        }

        /** 记录一个debug异常。  */
        open fun d(t: Throwable) {
            prepareLog(Log.DEBUG, t, null)
        }

        /** 记录一个使用可选格式args的info消息。  */
        open fun i(message: String, vararg args: Any) {
            prepareLog(Log.INFO, null, message, *args)
        }

        /** 记录一个info异常和使用可选格式args的消息。 */
        open fun i(t: Throwable, message: String, vararg args: Any) {
            prepareLog(Log.INFO, t, message, *args)
        }

        /** 记录一个info异常。  */
        open fun i(t: Throwable) {
            prepareLog(Log.INFO, t, null)
        }

        /** 记录一个使用可选格式args的warning消息。  */
        open fun w(message: String, vararg args: Any) {
            prepareLog(Log.WARN, null, message, *args)
        }

        /** 记录一个warning异常和使用可选格式args的消息。  */
        open fun w(t: Throwable, message: String, vararg args: Any) {
            prepareLog(Log.WARN, t, message, *args)
        }

        /** 记录一个warning异常。 */
        open fun w(t: Throwable) {
            prepareLog(Log.WARN, t, null)
        }

        /** 记录一个使用可选格式args的error消息。 */
        open fun e(message: String, vararg args: Any) {
            prepareLog(Log.ERROR, null, message, *args)
        }

        /** 记录一个error异常和使用可选格式args的消息。  */
        open fun e(t: Throwable, message: String, vararg args: Any) {
            prepareLog(Log.ERROR, t, message, *args)
        }

        /** 记录一个error异常。  */
        open fun e(t: Throwable) {
            prepareLog(Log.ERROR, t, null)
        }

        /** 记录一个使用可选格式args的assert消息 */
        open fun wtf(message: String, vararg args: Any) {
            prepareLog(Log.ASSERT, null, message, *args)
        }

        /** 记录一个assert异常和使用可选格式args的消息。  */
        open fun wtf(t: Throwable, message: String, vararg args: Any) {
            prepareLog(Log.ASSERT, t, message, *args)
        }

        /** 记录一个assert异常。 */
        open fun wtf(t: Throwable) {
            prepareLog(Log.ASSERT, t, null)
        }

        /** 记录一个使用可先格式args和指定priority的消息。 */
        open fun log(priority: Int, message: String, vararg args: Any) {
            prepareLog(priority, null, message, *args)
        }

        /** 记录一个异常和使用可先格式args和指定priority的消息。  */
        open fun log(priority: Int, t: Throwable, message: String, vararg args: Any) {
            prepareLog(priority, t, message, *args)
        }

        /** 记录一个指定priority的消息  */
        open fun log(priority: Int, t: Throwable) {
            prepareLog(priority, t, null)
        }

        /** 返回指定的tag或priority的消息是否应该被记录  */
        protected open fun isLoggable(tag: String, priority: Int): Boolean {
            return true
        }

        private fun prepareLog(priority: Int, t: Throwable?, message: String?, vararg args: Any) {
            var msg = message
            val tag = getTag() ?: "NullTag" // 即消息不可记录也要消费tag事件，以便下一个消息的tag是正确的

            if (msg.isNullOrBlank()) {
                if (t == null) {
                    return  // 如果消息是空的而且也没有异常对象，则忽略此消息
                }
                msg = getStackTraceString(t) // 获取异常的堆栈信息用作为消息
            } else {
                if (args.isNotEmpty()) {
                    // 如果消息使用了格式化，则先把消息进行格式化
                    msg = formatMessage(msg, args)
                }
                if (t != null) {
                    // 如果有异常对象，则把异常的堆栈信息拼接到消息的后面（换行显示）
                    msg += "\n${getStackTraceString(t)}"
                }
            }
            log(priority, tag, msg, t)
        }

        /** 使用可选参数格式化消息 */
        protected fun formatMessage(message: String, vararg args: Any): String {
            return String.format(message, *args)
        }

        /** 获取异常的堆栈信息 */
        private fun getStackTraceString(t: Throwable): String {
            // 不要用Log.getStackTraceString（）代替它，它隐藏了UnknownHostException，这不是我们想要的。
            val sw = StringWriter(256)
            val pw = PrintWriter(sw, false)
            t.printStackTrace(pw)
            pw.flush()
            return sw.toString()
        }

        /**
         * 将日志消息写入它的目的地。默认情况下，调用所有指定priority的方法。
         * @param priority 日志级别。有关常量，请参见[Log]。
         * @param tag 显式或推断的tag。
         * @param message 格式化的日志消息，可能为null，如果消息为null则Throwable就不能为null
         * @param t 伴随的异常，可能为null，如果异常为null，则message就不能为null
         */
        protected abstract fun log(priority: Int, tag: String, message: String?, t: Throwable?)
    }

    /** 用于调试的[Tree]。自动从调用类中推断出tag。  */
    open class DebugTree : Tree() {
        /**
         * 从element中提取用于消息的tag。 默认情况下，将使用没有任何匿名类后缀(如Foo$1变成Foo)的类名。
         * 注意：如果手动设置了一个[Timber.tag(String)][Timber.tag]，则此方法不会被调用
         */
        protected fun createStackElementTag(element: StackTraceElement): String {
            var tag = element.className
            val m = ANONYMOUS_CLASS.matcher(tag)
            if (m.find()) {
                tag = m.replaceAll("") // 如果tag中包含匿名类，则把匿名类替换为空字符串
            }
            tag = tag.substring(tag.lastIndexOf('.') + 1) // 取现类名（不要包名）
            // tag长度限制已在API 24中删除。
            return if (tag.length <= MAX_TAG_LENGTH || Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                tag
            } else tag.substring(0, MAX_TAG_LENGTH) // 最大tag长度为23个字符
        }

        override fun getTag(): String? {
            val tag = super.getTag() // 取出用户手动设置的tag
            if (tag != null) {
                return tag // 优先使用用户手动设置的tag
            }

            // 请不要把这个切换为Thread.getCurrentThread().getStackTrace()，Robolectric在JVM上运行它们测试通过，但是在Android元素上是不一样的。
            val stackTrace = Throwable().stackTrace
            check(stackTrace.size > CALL_STACK_INDEX) { "合成的堆栈跟踪没有足够的元素：您是否正在使用代码混淆？" }
            return createStackElementTag(stackTrace[CALL_STACK_INDEX]) // 从堆栈信息的第5层信息中取出类名作为tag
        }

        /**
         * 将“消息”分解为最大长度的块（如果需要）并发送给[Log.println()][Log.println]或[Log.wtf()][Log.wtf]
         * {@inheritDoc}
         */
        override fun log(priority: Int, tag: String, message: String?, t: Throwable?) {
            var msg = message!!  // 注意：message中已经拼接上异常对象的堆栈信息了，所以不可能为空的
            val needWriteToFile = msg.startsWith(WRITE_FILE_FLAG)
            if (needWriteToFile || isWriteAllLog()) {
                msg = msg.replace(WRITE_FILE_FLAG, "")
                if (!isUseSdcardRootPath || TimberConfig.hasStoragePermission()) {
                    mLogback?.let { logback ->
                        val msgForLogback = "$tag：$msg" // 构建一个用于logback的消息
                        when (priority) {
                            Log.VERBOSE -> logback.trace(msgForLogback)
                            Log.DEBUG -> logback.debug(msgForLogback)
                            Log.INFO -> logback.info(msgForLogback)
                            Log.WARN -> logback.warn(msgForLogback)
                            Log.ERROR -> {
                                if (t != null) {
                                    logback.warn("$tag：请在bug文件中查看此异常： ${t.javaClass.name}(${t.message})")
                                    if (needWriteToFile) { // Timber.e()的日志不写到bug.txt，Timber.fe()才写到bug.txt
                                        logback.error(msgForLogback) // error级别的日志会单独保存在一个Bug文件中。
                                    }
                                    //logger.error(msgForLogback, t); // Timber对象已经把异常数据都封装到message对象中了，所以不需要用这个方法
                                } else {
                                    logback.warn(msgForLogback)
                                    if (needWriteToFile) {// Timber.e()的日志不写到bug.txt，Timber.fe()才写到bug.txt
                                        logback.error(msgForLogback) // error级别的日志会单独保存在一个Bug文件中。
                                    }
                                }
                            }
                        }
                    }
                }
            }

            if (!isLoggable(tag, priority)) {
                return
            }

            val myTag = if (isAppendTimberPrefix) "Timber_$tag" else tag
            if (msg.length < MAX_LOG_LENGTH) {
                if (priority == Log.ASSERT) {
                    Log.wtf(myTag, msg)
                } else {
                    Log.println(priority, myTag, msg)
                }
                return
            }

            // 按行拆分，然后确保每行都可以容纳Log的最大长度。
            var i = 0
            val length = msg.length
            while (i < length) {
                var newline = msg.indexOf('\n', i)
                newline = if (newline != -1) newline else length
                do {
                    val end = newline.coerceAtMost(i + MAX_LOG_LENGTH)
                    val part = msg.substring(i, end)
                    if (priority == Log.ASSERT) {
                        Log.wtf(tag, part)
                    } else {
                        Log.println(priority, tag, part)
                    }
                    i = end
                } while (i < newline)
                i++
            }
        }

        companion object {
            private const val MAX_LOG_LENGTH = 4000
            private const val MAX_TAG_LENGTH = 23
            private const val CALL_STACK_INDEX = 5
            private val ANONYMOUS_CLASS = Pattern.compile("(\\$\\d+)+$")
        }
    }

    /**
     * Log开关控制类，默认只输出error级别的Log，其他级别log的输出通过setLogSwitch(boolean)进行设置，
     * 输出当天有效，第二天恢复只输出error级别的log。
     * 在使用此对象之前，必须先调用init(context)函数，建议在Application中进行调用
     */
    class DefaultTree: DebugTree() {

        private val isShowLogKey: String = "isShowLogKey"
        private val isWriteAllLogKey: String = "isWriteAllLogKey"
        private val isCloseAllConsoleLogKey: String = "isCloseAllConsoleLogKey"
        /** 用于获取保存配置的对象（SharedPreferences） */
        private lateinit var context: Application
        /** 用于保存当天的日期 */
        private var mToday: CharSequence? = null
        /** 用于控制是否显示Log（Debug版本依然会输出Log） */
        private var mIsShowLog = false
        /** 用于控制是否把所有的日志都写到文件 */
        private var mIsWriteAllLog = false
        /** 用于控制是否需要关闭所有的Log（不论当前是否是Debug版本） */
        private var mIsCloseAllConsoleLog = false
        private var debuggable = false

        /** 使用AES进行加密，加密后的数据使用Base64编码为String */
        fun encrypt(rawData: String): String = Base64.encodeToString(getCipher(Cipher.ENCRYPT_MODE).doFinal(rawData.toByteArray()), Base64.NO_WRAP)

        /** 把AES加密并通过Base64编码的String进行解密，还原为原始的String */
        fun decrypt(base64Data: String): String = String(getCipher(Cipher.DECRYPT_MODE).doFinal(Base64.decode(base64Data, Base64.NO_WRAP)))

        @SuppressLint("GetInstance")
        private fun getCipher(mode: Int) = Cipher.getInstance("AES/ECB/PKCS5Padding").apply { init(mode, SecretKeySpec("abc3efgabcdef119".toByteArray(), "AES")) }

        /**
         * 初始化LogTree, 在使用此对象之前，必须先调用init(context)函数，建议在Application中进行调用
         * @param context 用于获取保存配置的对象（SharedPreferences）
         * @param debuggable 是否是可调试的，建议传BuildConfig.DEBUG
         */
        fun init(context: Application, debuggable: Boolean) {
            this.context = context
            this.debuggable = debuggable
        }

        /** 设置是否显示Log，并持久化该参数，且只有当天有效，第二天自动变成不显示Log */
        @Deprecated("过时了", ReplaceWith("setIsShowLog(Boolean)"))
        fun setLogSwitch(isShowLog: Boolean) {
            setIsShowLog(isShowLog)
        }

        fun setIsShowLog(isShowLog: Boolean) {
            isAppendTimberPrefix = isShowLog // 如果通过这个方法来设置显示Log的话，说明是Release版本的，则日志的TAG中添加上Timber前缀，方便使用Timber来过滤出所有的日志。
            mIsShowLog = isShowLog
            putString("$isShowLogKey${getToday()}", isShowLog.toString())
        }

        /** 设置是否需要把所有的日志都写到文件 */
        fun setIsWriteAllLog(isWriteAllLog: Boolean) {
            mIsWriteAllLog = isWriteAllLog
            putString("$isWriteAllLogKey${getToday()}", isWriteAllLog.toString())
        }

        /** 判断是否需要把所有的日志都写到文件 */
        fun isWriteAllLog(): Boolean {
            val today = getToday()
            if (today != mToday) {
                mToday = today
                mIsWriteAllLog = getString("$isWriteAllLogKey${getToday()}") == "true" // 如果一天已经过去了，则今天也会变，所以不能写死
            }
            return mIsWriteAllLog
        }

        /** 获取Log开关，true为显示Log，false则不显示 */
        @Deprecated("过时了", ReplaceWith("isShowLog()"))
        fun getLogSwitch(): Boolean {
            return isShowLog()
        }

        /** 判断是否需要显示Log（如果是Debug版本，即使返回ture也会输出Log） */
        fun isShowLog(): Boolean {
            val today = getToday()
            if (today != mToday) {
                mToday = today
                mIsShowLog = getString("$isShowLogKey${getToday()}") == "true" // 如果一天已经过去了，则今天也会变，所以不能写死
            }
            return mIsShowLog
        }

        /** 设置是否要关闭所有输出到控制台的Log(不论是否是Debug版本) */
        fun setIsCloseAllConsoleLog(isCloseAllConsoleLog: Boolean) {
            mIsCloseAllConsoleLog = isCloseAllConsoleLog
            putString("$isCloseAllConsoleLogKey${getToday()}", isCloseAllConsoleLog.toString())
        }

        /** 判断是否需要关闭所有输出到控制台的log */
        fun isCloseAllConsoleLog(): Boolean {
            val today = getToday()
            if (today != mToday) {
                mToday = today
                mIsCloseAllConsoleLog = getString("$isCloseAllConsoleLogKey${getToday()}") == "true" // 如果一天已经过去了，则今天也会变，所以不能写死
            }
            return mIsCloseAllConsoleLog
        }

        /** 根据log级别判断是否输出log, error级别的log总是要显示的, 其他级别的Log是否输出要取决于设置的log开关 */
        override fun isLoggable(tag: String, priority: Int) : Boolean {
            if (isCloseAllConsoleLog()) {
                return false
            }

            return if (debuggable || priority == Log.ERROR) true else isShowLog()
        }

        /** 切换Log的显示为相反状态 */
        @Deprecated("过时了", ReplaceWith("showLogToggle()"))
        fun logToggle() = showLogToggle()
        /** 切换是否要显示日志的开关，Debug状态下不受此开关影响始终输出日志 */
        fun showLogToggle() = setLogSwitch(!mIsShowLog)
        /** 切换是否要把所有日志写到文件的开关 */
        fun writeAllLogToggle() = setIsWriteAllLog(!mIsWriteAllLog)
        /** 切换是否要关闭所有控制台日志的开关，Debug状态下也受此开关的影响 */
        fun closeAllConsoleLogToggle() = setIsCloseAllConsoleLog(!mIsCloseAllConsoleLog)

        /** 获取今天 */
        private fun getToday() = DateFormat.format("yyyy-MM-dd", Date())

        /** 存一个String到配置文件中 */
        private fun putString(key: String, value: String) {
            PreferenceManager.getDefaultSharedPreferences(context).edit().putString(encrypt(key), encrypt(value)).apply()
        }

        /** 从配置文件中取出一个String */
        private fun getString(key: String): String? {
            return PreferenceManager.getDefaultSharedPreferences(context).getString(encrypt(key), null)?.let { decrypt(it) }
        }

    }

    companion object {
        /** 写文件的标志，用于logback */
        private const val WRITE_FILE_FLAG = "--file--"

        /** logback，用于把日志写到文件中 */
        private var mLogback: Logger? = null

        lateinit var application: Application

        private var writeAllLog = false
        var isUseSdcardRootPath = false

        private var clickCount = 0
        private var longClickCount = 0
        private var closeAllConsoleLogCount = 0
        var isAppendTimberPrefix = false

        /** 记录一个使用可选格式args的verbose消息。  */
        fun v(@NonNls message: String, vararg args: Any) {
            TREE_OF_SOULS.v(message, *args)
        }

        /** 记录一个使用可选格式args的verbose消息，并写入日志文件。  */
        fun fv(@NonNls message: String, vararg args: Any) {
            TREE_OF_SOULS.v(WRITE_FILE_FLAG + message, *args)
        }

        /** 记录一个verbose的异常和使用可选格式args的消息。  */
        fun v(t: Throwable, @NonNls message: String, vararg args: Any) {
            TREE_OF_SOULS.v(t, message, *args)
        }

        /** 记录一个verbose的异常和使用可选格式args的消息，并写入日志文件。 */
        fun fv(t: Throwable, @NonNls message: String, vararg args: Any) {
            TREE_OF_SOULS.v(t, WRITE_FILE_FLAG + message, *args)
        }

        /** 记录一个verbose异常。  */
        fun v(t: Throwable) {
            TREE_OF_SOULS.v(t)
        }

        /** 记录一个verbose异常，并写入日志文件。  */
        fun fv(t: Throwable) {
            TREE_OF_SOULS.v(t, WRITE_FILE_FLAG)
        }

        /** 记录一个使用可选格式args的debug消息。  */
        fun d(@NonNls message: String, vararg args: Any) {
            TREE_OF_SOULS.d(message, *args)
        }

        /** 记录一个使用可选格式args的debug消息，并写入日志文件。  */
        fun fd(@NonNls message: String, vararg args: Any) {
            TREE_OF_SOULS.d(WRITE_FILE_FLAG + message, *args)
        }

        /** 记录一个debug异常和使用可选格式args的消息。  */
        fun d(t: Throwable, @NonNls message: String, vararg args: Any) {
            TREE_OF_SOULS.d(t, message, *args)
        }

        /** 记录一个debug异常和使用可选格式args的消息，并写入日志文件。  */
        fun fd(t: Throwable, @NonNls message: String, vararg args: Any) {
            TREE_OF_SOULS.d(t, WRITE_FILE_FLAG + message, *args)
        }

        /** 记录一个debug异常 */
        fun d(t: Throwable) {
            TREE_OF_SOULS.d(t)
        }

        /** 记录一个debug异常，并写入日志文件。 */
        fun fd(t: Throwable) {
            TREE_OF_SOULS.d(t, WRITE_FILE_FLAG)
        }

        /** 记录一个使用可选格式args的info消息。  */
        fun i(@NonNls message: String, vararg args: Any) {
            TREE_OF_SOULS.i(message, *args)
        }

        /** 记录一个使用可选格式args的info消息，并写入日志文件。  */
        fun fi(@NonNls message: String, vararg args: Any) {
            TREE_OF_SOULS.i(WRITE_FILE_FLAG + message, *args)
        }

        /** 记录一个info异常和使用可选格式args的消息。 */
        fun i(t: Throwable, @NonNls message: String, vararg args: Any) {
            TREE_OF_SOULS.i(t, message, *args)
        }

        /** 记录一个info异常和使用可选格式args的消息，并写入日志文件。 */
        fun fi(t: Throwable, @NonNls message: String, vararg args: Any) {
            TREE_OF_SOULS.i(t, WRITE_FILE_FLAG + message, *args)
        }

        /** 记录一个info异常。  */
        fun i(t: Throwable) {
            TREE_OF_SOULS.i(t)
        }

        /** 记录一个info异常，并写入日志文件。  */
        fun fi(t: Throwable) {
            TREE_OF_SOULS.i(t, WRITE_FILE_FLAG)
        }

        /** 记录一个使用可选格式args的warning消息。   */
        fun w(@NonNls message: String, vararg args: Any) {
            TREE_OF_SOULS.w(message, *args)
        }

        /** 记录一个使用可选格式args的warning消息，并写入日志文件。 */
        fun fw(@NonNls message: String, vararg args: Any) {
            TREE_OF_SOULS.w(WRITE_FILE_FLAG + message, *args)
        }

        /** 记录一个warning异常和使用可选格式args的消息。  */
        fun w(t: Throwable, @NonNls message: String, vararg args: Any) {
            TREE_OF_SOULS.w(t, message, *args)
        }

        /** 记录一个warning异常和使用可选格式args的消息，并写入日志文件。  */
        fun fw(t: Throwable, @NonNls message: String, vararg args: Any) {
            TREE_OF_SOULS.w(t, WRITE_FILE_FLAG + message, *args)
        }

        /** 记录一个warning异常。 */
        fun w(t: Throwable) {
            TREE_OF_SOULS.w(t)
        }

        /** 记录一个warning异常，并写入日志文件。 */
        fun fw(t: Throwable) {
            TREE_OF_SOULS.w(t, WRITE_FILE_FLAG)
        }

        /** 记录一个使用可选格式args的error消息。 */
        fun e(@NonNls message: String, vararg args: Any) {
            TREE_OF_SOULS.e(message, *args)
        }

        /** 记录一个使用可选格式args的error消息，并写入日志文件。 */
        fun fe(@NonNls message: String, vararg args: Any) {
            TREE_OF_SOULS.e(WRITE_FILE_FLAG + message, *args)
        }

        /** 记录一个error异常和使用可选格式args的消息。  */
        fun e(t: Throwable, @NonNls message: String, vararg args: Any) {
            TREE_OF_SOULS.e(t, message, *args)
        }

        /** 记录一个error异常和使用可选格式args的消息，并写入日志文件。 */
        fun fe(t: Throwable, @NonNls message: String, vararg args: Any) {
            TREE_OF_SOULS.e(t, WRITE_FILE_FLAG + message, *args)
        }

        /** 记录一个error异常。*/
        fun e(t: Throwable) {
            TREE_OF_SOULS.e(t)
        }

        /** 记录一个error异常，并写入日志文件。*/
        fun fe(t: Throwable) {
            TREE_OF_SOULS.e(t, WRITE_FILE_FLAG)
        }

        /** 记录一个使用可选格式args的assert消息 */
        fun wtf(@NonNls message: String, vararg args: Any) {
            TREE_OF_SOULS.wtf(message, *args)
        }

        /** 记录一个使用可选格式args的assert消息，并写入日志文件。 */
        fun fwtf(@NonNls message: String, vararg args: Any) {
            TREE_OF_SOULS.wtf(WRITE_FILE_FLAG + message, *args)
        }

        /** 记录一个assert异常和使用可选格式args的消息。  */
        fun wtf(t: Throwable, @NonNls message: String, vararg args: Any) {
            TREE_OF_SOULS.wtf(t, message, *args)
        }

        /** 记录一个assert异常和使用可选格式args的消息，并写入日志文件。 */
        fun fwtf(t: Throwable, @NonNls message: String, vararg args: Any) {
            TREE_OF_SOULS.wtf(t, WRITE_FILE_FLAG + message, *args)
        }

        /** 记录一个assert异常。 */
        fun wtf(t: Throwable) {
            TREE_OF_SOULS.wtf(t)
        }

        /** 记录一个assert异常，并写入日志文件。 */
        fun fwtf(t: Throwable) {
            TREE_OF_SOULS.wtf(t, WRITE_FILE_FLAG)
        }

        /** 记录一个使用可先格式args和指定priority的消息。 */
        fun log(priority: Int, @NonNls message: String, vararg args: Any) {
            TREE_OF_SOULS.log(priority, message, *args)
        }

        /** 记录一个异常和使用可先格式args和指定priority的消息。  */
        fun log(priority: Int, t: Throwable, @NonNls message: String, vararg args: Any) {
            TREE_OF_SOULS.log(priority, t, message, *args)
        }

        /** 记录一个指定priority的消息  */
        fun log(priority: Int, t: Throwable) {
            TREE_OF_SOULS.log(priority, t)
        }

        /**
         * A view into Timber's planted trees as a tree itself. This can be used for injecting a logger
         * instance rather than using static methods or to facilitate testing.
         * 视图进入木材真实植树作为树本身。 这可以用于注入一个记录器实例，而不是使用静态方法或方便测试。
         */
        fun asTree(): Tree {
            return TREE_OF_SOULS
        }

        /** 设置一个一次性tag，用于下一个日志记录调用。  */
        fun tag(tag: String): Tree {
            for (tree in forestAsArray) {
                tree.explicitTag.set(tag)
            }
            return TREE_OF_SOULS
        }

        private var defaultTree: DefaultTree? = null

        /** 设置是否显示Log，并持久化该参数，且只有当天有效，第二天自动变成不显示Log。注：只对DefaultTree生效 */
        @Deprecated("过时了", ReplaceWith("setIsShowLog(Boolean)"))
        fun setLogSwitch(isShowLog: Boolean) {
            setIsShowLog(isShowLog)
        }

        /** 设置是否显示Log，并持久化该参数，且只有当天有效，第二天自动变成不显示Log。注：只对DefaultTree生效 */
        fun setIsShowLog(isShowLog: Boolean) {
            defaultTree?.setIsShowLog(isShowLog)
        }

        /** 获取Log开关，true为显示Log，false则不显示。注：只对DefaultTree生效  */
        @Deprecated("过时了，请用isShowLog代替", ReplaceWith("isShowLog"))
        fun getLogSwitch(): Boolean {
            return isShowLog()
        }

        /** 获取Log开关，true为显示Log，false则不显示。注：只对DefaultTree生效  */
        fun isShowLog() = defaultTree?.isShowLog() ?: false

        /** 切换Log的显示为相反状态。注：只对DefaultTree生效 */
        @Deprecated("过时了", ReplaceWith("showLogToggle()"))
        fun logToggle() = showLogToggle()

        /** 切换开关（是否显示Log，Debug版本不受此开关影响，始终输出） */
        fun showLogToggle() = defaultTree?.showLogToggle()

        /** 设置是否需要把所有的日志都写到文件 */
        fun setIsWriteAllLog(isWriteAllLog: Boolean) {
            defaultTree?.setIsWriteAllLog(isWriteAllLog)
        }

        /** 判断是否需要把所有的日志都写到文件 */
        fun isWriteAllLog(): Boolean {
            return defaultTree?.isWriteAllLog() ?: false
        }

        /** 切换开关（是否把所有的日志都写到文件） */
        fun writeAllLogToggle() = defaultTree?.writeAllLogToggle()

        /** 设置是否要关闭所有输出到控制台的Log(不论是否是Debug版本) */
        fun setIsCloseAllConsoleLog(isCloseAllConsoleLog: Boolean) {
            defaultTree?.setIsCloseAllConsoleLog(isCloseAllConsoleLog)
        }

        /** 判断是否需要关闭所有输出到控制台的log */
        fun isCloseAllConsoleLog(): Boolean {
            return defaultTree?.isCloseAllConsoleLog() ?: false
        }

        /** 切换开关（是否要关闭所有控制台日志的开关） */
        fun closeAllConsoleLogToggle() = defaultTree?.closeAllConsoleLogToggle()

        /**
         * 植入一棵默认的Tree。在使用Timer记录日志之前，必须先植入一棵树。也可调用[Timber.plant]来植入其它的树
         * @param context 用于获取保存配置的对象（SharedPreferences）
         * @param buildConfigClass 传入自己项目中的BuildConfig类，Timber会根据BuildConfig.DEBUG来决定是否显示日志
         * @param isUseSdcardRootPath 指定是否要使用Sdcard为根目录保存日志，如果为true，则保存位置为：/sdcard/包名/ 目录下，否则保存位置为：/sdcard/Android/data/包名/files/Documents/ 目录下。注：前者需要存储权限，后者不需要。
         * @param graylogSource 指定GrayLog的来源，如不需要可不传
         * @param graylogHost 指定GrayLog的服务器地址，如不需要可不传
         * @param graylogPort 指定GrayLog的服务器端口，如不需要可不传
         * @param graylogSuffixPattern 指定GrayLog的日志前缀，如不需要可不传
         */
        fun init(context: Application, buildConfigClass: Class<*>, isUseSdcardRootPath: Boolean = false, graylogSource: String = "", graylogHost: String = "", graylogPort: Int = 0, graylogSuffixPattern: String = "") {
            if (buildConfigClass.simpleName != "BuildConfig") {
                error("非法参数，请传入自己项目中的BuildConfig类")
            }
            if (buildConfigClass.`package` == Timber::class.java.`package`) {
                error("不能使用日志库中的BuildConfig类（${BuildConfig::class.java.name}），请使用自己项目中的BuildConfig类")
            }

            application = context
            ContextHolder.init(context)
            Timber.isUseSdcardRootPath = isUseSdcardRootPath

            //初始化路径
            @Suppress("deprecation")
            TimberConfig.rootPath = Environment.getExternalStorageDirectory().absolutePath
            TimberConfig.packName = context.packageName

            // Timber初始化
            if (defaultTree == null) {
                // 此方法会自动创建外部存储的应用目录，只能通过此函数调用自动创建，不能手动创建。Logback创建目录时没有调用此方法，所以在没有生成应用目录时，Logback创建目录会失败。
                // 必须在调用Logback的代码之前先调用这个函数，以生成应用目录，这样Logback在此目录的基础上再创建其他目录时才能创建成功！
                if (!isUseSdcardRootPath) {
                    context.getExternalFilesDir(null)
                }
                defaultTree = DefaultTree()
                val field = buildConfigClass.getField("DEBUG")
                val debuggable: Boolean = field.getBoolean(null)
                defaultTree!!.init(context, debuggable)
                plant(defaultTree!!)
                Log.i(Timber::class.java.simpleName, "Timber初始化完成！")
            }

            // Logback初始化
            if (mLogback == null) {
                if (isUseSdcardRootPath && !TimberConfig.hasStoragePermission()) {
                    // 如果是使用SD卡根目录来存储日志，且没有存储权限，则没必要初始化Logback
                    Log.e(Timber::class.java.simpleName, "使用SD卡根目录保存日志，但是没有存储权限")
                    return
                }

                fixBugFileProblem()

                TimberConfig.pushService = graylogHost.isNotBlank() && graylogPort != 0
                TimberConfig.graylogHost = graylogHost
                TimberConfig.graylogPort = graylogPort
                TimberConfig.setGraylogSource(graylogSource)
                TimberConfig.graylogSuffixPattern = graylogSuffixPattern

                //动态加载配置文件(注：需要把asserts目录中的logback.xml文件删除或者改名，否则两个文件的设置都会生效)
                configureLogbackByString()

                mLogback = LoggerFactory.getLogger(Timber::class.java)
                Log.i(Timber::class.java.simpleName, "Logback初始化完成！")
            }

        }

        /** 修复Bug文件存在的问题 */
        private fun fixBugFileProblem() {
            // 因为Logback需要在第二天产生新Bug时，才会把第一天产生的bug.txt重命名为bug_2021_02_28.txt，如果第二天不产生Bug就不好了
            // 所以这里手动实现重命名。注：当Logback初始化的时候，会自动创建出bug.txt文件，如果你此时删除此文件，即使再手动创建一个，则Logback也不会写日志到文件里面了
            // 所以，我们需要在Logback初始化之前修改bug.txt为具体日期的文件，然后再初始化Logback，这样Logback就会再自动创建一个bug.txt文件，这样写日志就不会有问题
            val bugFile = File(TimberConfig.logDir, "/bugs/bug.txt")
            val format = "yyyy_MM_dd"
            val currentDate = DateFormat.format(format, System.currentTimeMillis())
            val fileLastModifiedDate = DateFormat.format(format, bugFile.lastModified())
            val exists = bugFile.exists()

            if (!exists || currentDate == fileLastModifiedDate) {
                // 如果bug.txt文件不存在，或者如果存在且修改日期和当前日期相同，则不需要做任何处理。
                return
            }

            // bug.txt文件存在，且文件的最后修改日期和当天不相等。
            // 如果不是当天的文件，需要我们手动重命名。
            if (bugFile.length() == 0L) {
                // 如果bug文件是空的，则直接删除让其重新创建一个新的就好了，就不需要手动重命名了。
                bugFile.delete()
            } else {
                val renameFile = File(bugFile.parentFile, "bug_$fileLastModifiedDate.txt")
                var renameResult = false
                try {
                    renameResult = bugFile.renameTo(renameFile)
                } catch (e: Exception) {
                    Log.i(Timber::class.java.simpleName, "重命名Bug文件时出现异常", e)
                }
                if (renameResult) {
                    Log.i(Timber::class.java.simpleName, "重命名Bug文件成功，新文件名为：" + renameFile.name)
                } else {
                    Log.i(Timber::class.java.simpleName, "重命名Bug文件失败")
                }
            }
        }

        fun isLogConfigString(string: String, isShowTip: Boolean = true): Boolean {
            if (string == "#showLog=true") {
                setLogSwitch(true)
                Log.i(Timber::class.java.simpleName,"Timber = ${getLogSwitch()}")
                if (isShowTip) {
                    showToast(getLogSwitch().toString())
                }
                return true
            }

            if (string == "#showLog=false") {
                setLogSwitch(false)
                Log.i(Timber::class.java.simpleName,"Timber = ${getLogSwitch()}")
                if (isShowTip) {
                    showToast(getLogSwitch().toString())
                }
                return true
            }

            if (string == "#writeAllLog=true") {
                setIsWriteAllLog(true)
                Log.i(Timber::class.java.simpleName,"Timber = ${isWriteAllLog()}")
                if (isShowTip) {
                    showToast(isWriteAllLog().toString())
                }
                return true
            }

            if (string == "#writeAllLog=false") {
                setIsWriteAllLog(false)
                Log.i(Timber::class.java.simpleName,"Timber = ${isWriteAllLog()}")
                if (isShowTip) {
                    showToast(isWriteAllLog().toString())
                }
                return true
            }

            return false
        }

        /**
         * 绑定showLogToggle()的调用到指定的View上。
         * 长按指定的view 1次，再短按指定的View7次就会触发showLogToggle()的调用。
         * 长按指定的view 2次（或更多），再短按指定的View7次就会触发writeAllLogToggle()的调用。
         * @param view
         * @param isShowTip 是否需要Toast提示当前的开关状态
         */
        fun bindLogToggleToView(view: View, isShowTip: Boolean = true) {
            view.setOnClickListener {
                if (++clickCount == 7) {
                    when {
                        longClickCount == 1 -> {
                            showLogToggle()     // 显示所有级别的Log，即使是Release版本也显示
                            Log.i(Timber::class.java.simpleName, "showLog = ${isShowLog()}")
                            if (isShowTip) {
                                showToast("showLog = ${isShowLog()}")
                            }
                        }
                        longClickCount in 2..4 -> {
                            writeAllLogToggle() // 把所有Log都写到文件，即使调用不带f的函数的log也写到日志
                            Log.i(Timber::class.java.simpleName, "writeAllLog = ${isWriteAllLog()}")
                            if (isShowTip) {
                                showToast("writeAllLog = ${isWriteAllLog()}")
                            }
                        }
                        longClickCount >= 5 -> {
                            closeAllConsoleLogToggle() // 所有输出到控制台的日志都不再显示，写到日志文件的没影响，只是不输出到控制台而已。
                            Log.i(Timber::class.java.simpleName, "closeAllConsoleLog = ${isCloseAllConsoleLog()}")
                            if (isShowTip) {
                                showToast("closeAllConsoleLog = ${isCloseAllConsoleLog()}")
                            }
                        }
                    }

                    clickCount = 0
                    longClickCount = 0
                }
            }

            view.setOnLongClickListener {
                ++longClickCount
                true
            }
        }

        fun showToast(text: CharSequence, gravity: Int = Gravity.CENTER, duration: Int = Toast.LENGTH_SHORT, xOffset: Int = 0, yOffset: Int = 0) {
            Toast.makeText(application, text, duration).apply {
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
                    // Android R （API 11）或更高版本时，此函数的调用没有效果，而且在Log中会出现警告
                    setGravity(gravity, xOffset, yOffset)
                }
            }.show()
        }

        /** 添加一个新的日志树。  */
        // Validating public API contract.（验证公共API合同。）
        fun plant(tree: Tree) {
            require(!(tree === TREE_OF_SOULS)) { "不能将Timber植入它自身。" }
            synchronized(FOREST) {
                FOREST.add(tree) // 把树放入集合中
                forestAsArray = FOREST.toTypedArray() // 把集合转换为对应的数组
            }
        }

        /** 添加新的日志记录树。  */
        // Validating public API contract.（验证公共API合同。）
        fun plant(vararg trees: Tree) {
            for (tree in trees) {
                require(!(tree === TREE_OF_SOULS)) { "不能将Timber植入它自身。" }
            }
            synchronized(FOREST) {
                Collections.addAll(FOREST, *trees)
                forestAsArray = FOREST.toTypedArray()
            }
        }

        /** 拔掉一棵已经种下的树。*/
        fun uproot(tree: Tree) {
            synchronized(FOREST) {
                require(FOREST.remove(tree)) { "无法拔掉一棵没种下的树: $tree" }
                forestAsArray = FOREST.toTypedArray()
            }
        }

        /** 拔掉所有已经种下的树 */
        fun uprootAll() {
            synchronized(FOREST) {
                FOREST.clear()
                forestAsArray = TREE_ARRAY_EMPTY
            }
        }

        /** 返回所有已种植的[tree][Tree]的副本。 */
        fun forest(): List<Tree> {
            synchronized(FOREST) {
                return Collections.unmodifiableList(ArrayList(FOREST))
            }
        }

        /** 返回所有已种下的树的数量 */
        fun treeCount(): Int {
            synchronized(FOREST) { return FOREST.size }
        }

        /* 一个空的Tree数组 */
        private val TREE_ARRAY_EMPTY = arrayOf<Tree>()

        // 这两个字段都由“FOREST”守卫。
        private val FOREST: MutableList<Tree> = ArrayList()
        @Volatile var forestAsArray = TREE_ARRAY_EMPTY

        /** 灵魂之树，此Tree对象将会把工作委派给种植在[Timber.FOREST][Timber.FOREST]中的所有的[Tree] */
        private val TREE_OF_SOULS: Tree = object : Tree() {
            override fun v(message: String, vararg args: Any) {
                forestAsArray.forEach { it.v(message, *args) }
            }

            override fun v(t: Throwable, message: String, vararg args: Any) {
                forestAsArray.forEach { it.v(t, message, *args) }
            }

            override fun v(t: Throwable) {
                forestAsArray.forEach { it.v(t) }
            }

            override fun d(message: String, vararg args: Any) {
                forestAsArray.forEach { it.d(message, *args) }
            }

            override fun d(t: Throwable, message: String, vararg args: Any) {
                forestAsArray.forEach { it.d(t, message, *args) }
            }

            override fun d(t: Throwable) {
                forestAsArray.forEach { it.d(t) }
            }

            override fun i(message: String, vararg args: Any) {
                forestAsArray.forEach { it.i(message, *args) }
            }

            override fun i(t: Throwable, message: String, vararg args: Any) {
                forestAsArray.forEach { it.i(t, message, *args) }
            }

            override fun i(t: Throwable) {
                forestAsArray.forEach { it.i(t) }
            }

            override fun w(message: String, vararg args: Any) {
                forestAsArray.forEach { it.w(message, *args) }
            }

            override fun w(t: Throwable, message: String, vararg args: Any) {
                forestAsArray.forEach { it.w(t, message, *args) }
            }

            override fun w(t: Throwable) {
                forestAsArray.forEach { it.w(t) }
            }

            override fun e(message: String, vararg args: Any) {
                forestAsArray.forEach { it.e(message, *args) }
            }

            override fun e(t: Throwable, message: String, vararg args: Any) {
                forestAsArray.forEach { it.e(t, message, *args) }
            }

            override fun e(t: Throwable) {
                forestAsArray.forEach { it.e(t) }
            }

            override fun wtf(message: String, vararg args: Any) {
                forestAsArray.forEach { it.wtf(message, *args) }
            }

            override fun wtf(t: Throwable, message: String, vararg args: Any) {
                forestAsArray.forEach { it.wtf(t, message, *args) }
            }

            override fun wtf(t: Throwable) {
                forestAsArray.forEach { it.wtf(t) }
            }

            override fun log(priority: Int, message: String, vararg args: Any) {
                forestAsArray.forEach { it.log(priority, message, *args) }
            }

            override fun log(priority: Int, t: Throwable, message: String, vararg args: Any) {
                forestAsArray.forEach { it.log(priority, t, message, *args) }
            }

            override fun log(priority: Int, t: Throwable) {
                forestAsArray.forEach { it.log(priority, t) }
            }

            override fun log(priority: Int, tag: String, message: String?, t: Throwable?) {
                throw AssertionError("没有覆盖此方法，不应该调用此方法")
            }
        }
    }
}